home *** CD-ROM | disk | FTP | other *** search
/ Aminet 30 / Aminet 30 (1999)(Schatztruhe)[!][Apr 1999].iso / Aminet / gfx / misc / gnuplot-3.7src.lha / gnuplot-3.7src / gnuplot-3.7.lha / gnuplot-3.7 / util3d.c < prev    next >
C/C++ Source or Header  |  1998-09-22  |  33KB  |  1,260 lines

  1. #ifndef lint
  2. static char *RCSid = "$Id: util3d.c,v 1.17 1998/06/18 14:55:20 ddenholm Exp $";
  3. #endif
  4.  
  5.  
  6. /* GNUPLOT - util3d.c */
  7.  
  8. /*[
  9.  * Copyright 1986 - 1993, 1998   Thomas Williams, Colin Kelley
  10.  *
  11.  * Permission to use, copy, and distribute this software and its
  12.  * documentation for any purpose with or without fee is hereby granted,
  13.  * provided that the above copyright notice appear in all copies and
  14.  * that both that copyright notice and this permission notice appear
  15.  * in supporting documentation.
  16.  *
  17.  * Permission to modify the software is granted, but not the right to
  18.  * distribute the complete modified source code.  Modifications are to
  19.  * be distributed as patches to the released version.  Permission to
  20.  * distribute binaries produced by compiling modified sources is granted,
  21.  * provided you
  22.  *   1. distribute the corresponding source modifications from the
  23.  *    released version in the form of a patch file along with the binaries,
  24.  *   2. add special version identification to distinguish your version
  25.  *    in addition to the base release version number,
  26.  *   3. provide your name and address as the primary contact for the
  27.  *    support of your modified version, and
  28.  *   4. retain our contact information in regard to use of the base
  29.  *    software.
  30.  * Permission to distribute the released version of the source code along
  31.  * with corresponding source modifications in the form of a patch file is
  32.  * granted with same provisions 2 through 4 for binary distributions.
  33.  *
  34.  * This software is provided "as is" without express or implied warranty
  35.  * to the extent permitted by applicable law.
  36. ]*/
  37.  
  38.  
  39. /*
  40.  * 19 September 1992  Lawrence Crowl  (crowl@cs.orst.edu)
  41.  * Added user-specified bases for log scaling.
  42.  *
  43.  * 3.6 - split graph3d.c into graph3d.c (graph),
  44.  *                            util3d.c (intersections, etc)
  45.  *                            hidden3d.c (hidden-line removal code)
  46.  *
  47.  */
  48.  
  49. #include "plot.h"
  50. #include "setshow.h"
  51.  
  52. extern int xleft,xright,ybot,ytop;
  53. extern int hidden_active; /* HBB 980324: hidden_no_update was unused here */
  54.  
  55. /* ACCESS THINGS THAT OUGHT TO BE HIDDEN IN hidden3d.c - perhaps we
  56.  * can move the relevant code into hidden3d.c sometime
  57.  */
  58.  
  59. /* Bitmap of the screen.  The array for each x value is malloc-ed as needed */
  60.  
  61. extern int suppressMove;
  62.  
  63. extern double min_array[], max_array[];
  64. extern int auto_array[], log_array[];
  65. extern double base_array[], log_base_array[];
  66.  
  67. /* for convenience while converting to use these arrays */
  68. #define x_min3d min_array[FIRST_X_AXIS]
  69. #define x_max3d max_array[FIRST_X_AXIS]
  70. #define y_min3d min_array[FIRST_Y_AXIS]
  71. #define y_max3d max_array[FIRST_Y_AXIS]
  72. #define z_min3d min_array[FIRST_Z_AXIS]
  73. #define z_max3d max_array[FIRST_Z_AXIS]
  74. #define min3d_z min_array[FIRST_Z_AXIS]
  75. #define max3d_z max_array[FIRST_Z_AXIS]
  76.  
  77. typedef double transform_matrix[4][4];
  78.  
  79. void mat_unit(mat)
  80. transform_matrix mat;
  81. {
  82.     int i, j;
  83.  
  84.     for (i = 0; i < 4; i++) for (j = 0; j < 4; j++)
  85.     if (i == j)
  86.         mat[i][j] = 1.0;
  87.     else
  88.         mat[i][j] = 0.0;
  89. }
  90.  
  91. void mat_trans(tx, ty, tz, mat)
  92. double tx, ty, tz;
  93. transform_matrix mat;
  94. {
  95.      mat_unit(mat);                                 /* Make it unit matrix. */
  96.      mat[3][0] = tx;
  97.      mat[3][1] = ty;
  98.      mat[3][2] = tz;
  99. }
  100.  
  101. void mat_scale(sx, sy, sz, mat)
  102. double sx, sy, sz;
  103. transform_matrix mat;
  104. {
  105.      mat_unit(mat);                                 /* Make it unit matrix. */
  106.      mat[0][0] = sx;
  107.      mat[1][1] = sy;
  108.      mat[2][2] = sz;
  109. }
  110.  
  111. void mat_rot_x(teta, mat)
  112. double teta;
  113. transform_matrix mat;
  114. {
  115.     double cos_teta, sin_teta;
  116.  
  117.     teta *= Pi / 180.0;
  118.     cos_teta = cos(teta);
  119.     sin_teta = sin(teta);
  120.  
  121.     mat_unit(mat);                                  /* Make it unit matrix. */
  122.     mat[1][1] = cos_teta;
  123.     mat[1][2] = -sin_teta;
  124.     mat[2][1] = sin_teta;
  125.     mat[2][2] = cos_teta;
  126. }
  127.  
  128. void mat_rot_y(teta, mat)
  129. double teta;
  130. transform_matrix mat;
  131. {
  132.     double cos_teta, sin_teta;
  133.  
  134.     teta *= Pi / 180.0;
  135.     cos_teta = cos(teta);
  136.     sin_teta = sin(teta);
  137.  
  138.     mat_unit(mat);                                  /* Make it unit matrix. */
  139.     mat[0][0] = cos_teta;
  140.     mat[0][2] = -sin_teta;
  141.     mat[2][0] = sin_teta;
  142.     mat[2][2] = cos_teta;
  143. }
  144.  
  145. void mat_rot_z(teta, mat)
  146. double teta;
  147. transform_matrix mat;
  148. {
  149.     double cos_teta, sin_teta;
  150.  
  151.     teta *= Pi / 180.0;
  152.     cos_teta = cos(teta);
  153.     sin_teta = sin(teta);
  154.  
  155.     mat_unit(mat);                                  /* Make it unit matrix. */
  156.     mat[0][0] = cos_teta;
  157.     mat[0][1] = -sin_teta;
  158.     mat[1][0] = sin_teta;
  159.     mat[1][1] = cos_teta;
  160. }
  161.  
  162. /* Multiply two transform_matrix. Result can be one of two operands. */
  163. void mat_mult(mat_res, mat1, mat2)
  164. transform_matrix mat_res, mat1, mat2;
  165. {
  166.     int i, j, k;
  167.     transform_matrix mat_res_temp;
  168.  
  169.     for (i = 0; i < 4; i++) for (j = 0; j < 4; j++) {
  170.         mat_res_temp[i][j] = 0;
  171.         for (k = 0; k < 4; k++) mat_res_temp[i][j] += mat1[i][k] * mat2[k][j];
  172.     }
  173.     for (i = 0; i < 4; i++) for (j = 0; j < 4; j++)
  174.     mat_res[i][j] = mat_res_temp[i][j];
  175. }
  176.  
  177.  
  178. /* Test a single point to be within the xleft,xright,ybot,ytop bbox.
  179.  * Sets the returned integers 4 l.s.b. as follows:
  180.  * bit 0 if to the left of xleft.
  181.  * bit 1 if to the right of xright.
  182.  * bit 2 if above of ytop.
  183.  * bit 3 if below of ybot.
  184.  * 0 is returned if inside.
  185.  */
  186. int clip_point(x, y)
  187. unsigned int x, y;
  188. {
  189.     int ret_val = 0;
  190.  
  191.     if (x < xleft) ret_val |= 0x01;
  192.     if (x > xright) ret_val |= 0x02;
  193.     if (y < ybot) ret_val |= 0x04;
  194.     if (y > ytop) ret_val |= 0x08;
  195.  
  196.     return ret_val;
  197. }
  198.  
  199. /* Clip the given line to drawing coords defined as xleft,xright,ybot,ytop.
  200.  *   This routine uses the cohen & sutherland bit mapping for fast clipping -
  201.  * see "Principles of Interactive Computer Graphics" Newman & Sproull page 65.
  202.  */
  203. void draw_clip_line(x1, y1, x2, y2)
  204. unsigned int x1, y1, x2, y2;
  205. {
  206.     int x, y, dx, dy, x_intr[4], y_intr[4], count, pos1, pos2;
  207.     register struct termentry *t = term;
  208.  
  209. #if defined(ATARI) || defined(MTOS)
  210.     if(x1<0||x2<0||y1<0||y2<0) return; /* temp bug fix */
  211. #endif
  212.  
  213.     pos1 = clip_point(x1, y1);
  214.     pos2 = clip_point(x2, y2);
  215.     if (pos1 || pos2) {
  216.     if (pos1 & pos2) return;         /* segment is totally out. */
  217.  
  218.     /* Here part of the segment MAY be inside. test the intersection
  219.      * of this segment with the 4 boundaries for hopefully 2 intersections
  220.      * in. If none are found segment is totaly out.
  221.      * Under rare circumstances there may be up to 4 intersections (e.g.
  222.      * when the line passes directly through at least one corner). In
  223.      * this case it is sufficient to take any 2 intersections (e.g. the
  224.      * first two found).
  225.      */
  226.     count = 0;
  227.     dx = x2 - x1;
  228.     dy = y2 - y1;
  229.  
  230.     /* Find intersections with the x parallel bbox lines: */
  231.     if (dy != 0) {
  232.         x = (ybot - y2) * dx / dy + x2;      /* Test for ybot boundary. */
  233.         if (x >= xleft && x <= xright) {
  234.         x_intr[count] = x;
  235.         y_intr[count++] = ybot;
  236.         }
  237.         x = (ytop - y2) * dx / dy + x2;      /* Test for ytop boundary. */
  238.         if (x >= xleft && x <= xright) {
  239.         x_intr[count] = x;
  240.         y_intr[count++] = ytop;
  241.         }
  242.     }
  243.  
  244.     /* Find intersections with the y parallel bbox lines: */
  245.     if (dx != 0) {
  246.         y = (xleft - x2) * dy / dx + y2;    /* Test for xleft boundary. */
  247.         if (y >= ybot && y <= ytop) {
  248.         x_intr[count] = xleft;
  249.         y_intr[count++] = y;
  250.         }
  251.         y = (xright - x2) * dy / dx + y2;  /* Test for xright boundary. */
  252.         if (y >= ybot && y <= ytop) {
  253.         x_intr[count] = xright;
  254.         y_intr[count++] = y;
  255.         }
  256.     }
  257.  
  258.     if (count >= 2) {
  259.         int x_max, x_min, y_max, y_min;
  260.  
  261.         x_min = GPMIN(x1, x2);
  262.         x_max = GPMAX(x1, x2);
  263.         y_min = GPMIN(y1, y2);
  264.         y_max = GPMAX(y1, y2);
  265.  
  266.         if (pos1 && pos2) {              /* Both were out - update both */
  267.         x1 = x_intr[0];
  268.         y1 = y_intr[0];
  269.         x2 = x_intr[1];
  270.         y2 = y_intr[1];
  271.         }
  272.         else if (pos1) {         /* Only x1/y1 was out - update only it */
  273.         if (dx * (x2 - x_intr[0]) + dy * (y2 - y_intr[0]) > 0) {
  274.             x1 = x_intr[0];
  275.             y1 = y_intr[0];
  276.         }
  277.         else {
  278.             x1 = x_intr[1];
  279.             y1 = y_intr[1];
  280.         }
  281.         }
  282.         else {                    /* Only x2/y2 was out - update only it */
  283.         if (dx * (x_intr[0] - x1) + dy * (y_intr[0] - y1) > 0) {
  284.             x2 = x_intr[0];
  285.             y2 = y_intr[0];
  286.         }
  287.         else {
  288.             x2 = x_intr[1];
  289.             y2 = y_intr[1];
  290.         }
  291.         }
  292.  
  293.         if (x1 < x_min || x1 > x_max ||
  294.         x2 < x_min || x2 > x_max ||
  295.         y1 < y_min || y1 > y_max ||
  296.         y2 < y_min || y2 > y_max) return;
  297.     }
  298.     else
  299.         return;
  300.     }
  301.  
  302.     
  303. #ifndef LITE
  304.     if(hidden3d && hidden_active && draw_surface)
  305.       {
  306.         draw_line_hidden(x1, y1, x2, y2);
  307.         return;
  308.       };
  309. #endif /* not LITE */
  310.     if(!suppressMove) (*t->move)(x1,y1);
  311.     (*t->vector)(x2,y2);
  312. }
  313.  
  314.  
  315.  
  316. /* And text clipping routine. */
  317. void clip_put_text(x, y, str)
  318. unsigned int x,y;
  319. char *str;
  320. {
  321.     register struct termentry *t = term;
  322.  
  323.     if (clip_point(x, y)) return;
  324.  
  325.     (*t->put_text)(x,y,str);
  326. }
  327.  
  328. /* seems sensible to put the justification in here too..? */
  329. void clip_put_text_just(x,y,str,just)
  330. unsigned int x,y;
  331. char *str;
  332. enum JUSTIFY just;
  333. {
  334.         register struct termentry *t = term;
  335.     if (clip_point(x,y)) return;
  336.     if (!(*t->justify_text)(just)) {
  337.         assert(CENTRE == 1 && RIGHT == 2);
  338.         x -= (t->h_char * strlen(str) * just)/2;
  339.     }
  340.     (*t->put_text)(x,y,str);
  341. }
  342.  
  343.  
  344.  
  345. /* Clip the given line to drawing coords defined as xleft,xright,ybot,ytop.
  346.  *   This routine uses the cohen & sutherland bit mapping for fast clipping -
  347.  * see "Principles of Interactive Computer Graphics" Newman & Sproull page 65.
  348.  */
  349.  
  350. int clip_line(x1, y1, x2, y2)
  351.      int *x1, *y1, *x2, *y2;
  352. {
  353.   int x, y, dx, dy, x_intr[4], y_intr[4], count, pos1, pos2;
  354.   int x_max, x_min, y_max, y_min;
  355.   pos1 = clip_point(*x1, *y1);
  356.   pos2 = clip_point(*x2, *y2);
  357.   if (!pos1 && !pos2) return 1; /* segment is totally in */ 
  358.   if (pos1 & pos2) return 0;    /* segment is totally out. */ 
  359.   /* Here part of the segment MAY be inside. test the intersection
  360.    * of this segment with the 4 boundaries for hopefully 2 intersections
  361.    * in. If non found segment is totaly out.
  362.    */ 
  363.   count = 0;
  364.   dx = *x2 - *x1;  
  365.   dy = *y2 - *y1;
  366.   /* Find intersections with the x parallel bbox lines: */ 
  367.   if (dy != 0) {
  368.     x = (ybot - *y2) * dx / dy + *x2; /* Test for ybot boundary. */ 
  369.     if (x >= xleft && x <= xright) {
  370.       x_intr[count] = x;       
  371.       y_intr[count++] = ybot;
  372.     }
  373.     x = (ytop - *y2) * dx / dy + *x2; /* Test for ytop boundary. */ 
  374.     if (x >= xleft && x <= xright) {
  375.       x_intr[count] = x;       
  376.       y_intr[count++] = ytop;
  377.     }
  378.   }
  379.   /* Find intersections with the y parallel bbox lines: */ 
  380.   if (dx != 0) {
  381.     y = (xleft - *x2) * dy / dx + *y2; /* Test for xleft boundary. */
  382.     if (y >= ybot && y <= ytop) {
  383.       x_intr[count] = xleft;   
  384.       y_intr[count++] = y;
  385.     }
  386.     y = (xright - *x2) * dy / dx + *y2; /* Test for xright boundary. */
  387.     if (y >= ybot && y <= ytop) {
  388.       x_intr[count] = xright;  
  389.       y_intr[count++] = y;
  390.     }
  391.   }
  392.   if (count < 2) return 0;
  393.   if (*x1 < *x2)
  394.     x_min = *x1, x_max = *x2;
  395.   else
  396.     x_min = *x2, x_max = *x1;
  397.   if (*y1 < *y2)
  398.     y_min = *y1, y_max = *y2;
  399.   else
  400.     y_min = *y2, y_max = *y1;
  401.   if (pos1 && pos2) {        /* Both were out - update both */
  402.     *x1 = x_intr[0];     
  403.     *y1 = y_intr[0];
  404.     *x2 = x_intr[1];     
  405.     *y2 = y_intr[1];
  406.   }
  407.   else if (pos1) 
  408.     {                /* Only x1/y1 was out - update only it */
  409.       if (dx * (*x2 - x_intr[0]) + dy * (*y2 - y_intr[0]) >= 0) 
  410.     {    
  411.       *x1 = x_intr[0];   
  412.       *y1 = y_intr[0];  
  413.     }
  414.       else 
  415.     { 
  416.       *x1 = x_intr[1];   
  417.       *y1 = y_intr[1];  
  418.     }
  419.     }
  420.   else 
  421.     {                /* Only x2/y2 was out - update only it */
  422.       if (dx * (x_intr[0] - *x1) + dy * (y_intr[0] - *y1) >= 0) 
  423.     {    
  424.       *x2 = x_intr[0];   
  425.       *y2 = y_intr[0];  
  426.     }
  427.       else 
  428.     { 
  429.       *x2 = x_intr[1];   
  430.       *y2 = y_intr[1];  
  431.     }
  432.     }
  433.  
  434.   if (*x1 < x_min || *x1 > x_max ||  
  435.       *x2 < x_min || *x2 > x_max ||
  436.       *y1 < y_min || *y1 > y_max ||  
  437.       *y2 < y_min || *y2 > y_max) 
  438.     return 0;
  439.   return 1;
  440. }
  441.  
  442.  
  443. /* single edge intersection algorithm */
  444. /* Given two points, one inside and one outside the plot, return
  445.  * the point where an edge of the plot intersects the line segment defined 
  446.  * by the two points.
  447.  */
  448. void edge3d_intersect(points, i, ex, ey, ez)
  449.     struct coordinate GPHUGE *points; /* the points array */
  450.     int i;                /* line segment from point i-1 to point i */
  451.     double *ex, *ey, *ez;        /* the point where it crosses an edge */
  452. {
  453.     /* global x_min3d, x_max3d, y_min3d, y_max3d, min3d_z, max3d_z */
  454.     int count;
  455.     double ix = points[i-1].x;
  456.     double iy = points[i-1].y;
  457.     double iz = points[i-1].z;
  458.     double ox = points[i].x;
  459.     double oy = points[i].y;
  460.     double oz = points[i].z;
  461.     double x, y, z;            /* possible intersection point */
  462.  
  463.     if(points[i].type == INRANGE)
  464.       {
  465.     /* swap points around so that ix/ix/iz are INRANGE and ox/oy/oz are OUTRANGE */
  466.     x = ix;ix = ox;ox = x;
  467.     y = iy;iy = oy;oy = y;
  468.     z = iz;iz = oz;oz = z;
  469.       }
  470.  
  471.     /* nasty degenerate cases, effectively drawing to an infinity point (?)
  472.        cope with them here, so don't process them as a "real" OUTRANGE point 
  473.  
  474.        If more than one coord is -VERYLARGE, then can't ratio the "infinities"
  475.        so drop out by returning the INRANGE point.
  476.  
  477.        Obviously, only need to test the OUTRANGE point (coordinates) */
  478.  
  479.     /* nasty degenerate cases, effectively drawing to an infinity point (?)
  480.        cope with them here, so don't process them as a "real" OUTRANGE point 
  481.  
  482.        If more than one coord is -VERYLARGE, then can't ratio the "infinities"
  483.        so drop out by returning FALSE */
  484.     
  485.     count = 0;
  486.     if(ox == -VERYLARGE) count++;
  487.     if(oy == -VERYLARGE) count++;
  488.     if(oz == -VERYLARGE) count++;
  489.  
  490.     /* either doesn't pass through 3D volume *or* 
  491.        can't ratio infinities to get a direction to draw line, so return the INRANGE point */
  492.     if(count > 1){
  493.       *ex = ix;
  494.       *ey = iy;
  495.       *ez = iz;
  496.  
  497.       return;
  498.     }
  499.  
  500.     if(count == 1)
  501.       {
  502.     *ex = ix;
  503.     *ey = iy;
  504.     *ez = iz;
  505.  
  506.     if(ox == -VERYLARGE)
  507.       {
  508.         *ex = x_min3d;
  509.         return;
  510.       }
  511.  
  512.     if(oy == -VERYLARGE)
  513.       {
  514.         *ey = y_min3d;
  515.         return;
  516.       }
  517.  
  518.     /* obviously oz is -VERYLARGE and (ox != -VERYLARGE && oy != -VERYLARGE) */
  519.     *ez = min3d_z;
  520.     return;
  521.       }
  522.  
  523.     /*
  524.      * Can't have case (ix == ox && iy == oy && iz == oz) as one point
  525.      * is INRANGE and one point is OUTRANGE.
  526.      */
  527.     if(ix == ox) {
  528.       if(iy == oy) {
  529.     /* line parallel to z axis */
  530.  
  531.     /* assume inrange(iy, y_min3d, y_max3d) && inrange(ix, x_min3d, x_max3d) */
  532.     *ex = ix;        /* == ox */
  533.     *ey = iy;        /* == oy */
  534.     
  535.     if (inrange(max3d_z, iz, oz))
  536.       *ez = max3d_z;
  537.     else if (inrange(min3d_z, iz, oz))
  538.       *ez = min3d_z;
  539.     else {
  540.       graph_error("error in edge3d_intersect");
  541.         }
  542.  
  543.     return;
  544.       }
  545.       
  546.       if(iz == oz) {
  547.     /* line parallel to y axis */
  548.     
  549.     /* assume inrange(iz, min3d_z, max3d_z) && inrange(ix, x_min3d, x_max3d) */
  550.     *ex = ix;        /* == ox */
  551.     *ez = iz;        /* == oz */
  552.     
  553.     if (inrange(y_max3d, iy, oy))
  554.       *ey = y_max3d;
  555.     else if (inrange(y_min3d, iy, oy))
  556.       *ey = y_min3d;
  557.     else {
  558.       graph_error("error in edge3d_intersect");
  559.     }
  560.     
  561.     return;
  562.       }
  563.  
  564.       /* nasty 2D slanted line in a yz plane */
  565.       
  566.       /* does it intersect y_min3d edge */
  567.       if (inrange(y_min3d, iy, oy) && y_min3d != iy && y_min3d != oy) {
  568.     z = iz + (y_min3d-iy) * ((oz-iz) / (oy-iy));
  569.     if (inrange(z, min3d_z, max3d_z)) {
  570.       *ex = ix;
  571.       *ey = y_min3d;
  572.       *ez = z;
  573.       return;
  574.     }
  575.       }
  576.  
  577.       /* does it intersect y_max3d edge */
  578.       if (inrange(y_max3d, iy, oy) && y_max3d != iy && y_max3d != oy) {
  579.     z = iz + (y_max3d-iy) * ((oz-iz) / (oy-iy));
  580.     if (inrange(z, min3d_z, max3d_z)) {
  581.       *ex = ix;
  582.       *ey = y_max3d;
  583.       *ez = z;
  584.       return;
  585.     }
  586.       }
  587.  
  588.       /* does it intersect min3d_z edge */
  589.       if (inrange(min3d_z, iz, oz) && min3d_z != iz && min3d_z != oz) {
  590.     y = iy + (min3d_z-iz) * ((oy-iy) / (oz-iz));
  591.     if (inrange(y, y_min3d, y_max3d)) {
  592.       *ex = ix;
  593.       *ey = y;
  594.       *ez = min3d_z;
  595.       return;
  596.     }
  597.       }
  598.  
  599.       /* does it intersect max3d_z edge */
  600.       if (inrange(max3d_z, iz, oz) && max3d_z != iz && max3d_z != oz) {
  601.     y = iy + (max3d_z-iz) * ((oy-iy) / (oz-iz));
  602.     if (inrange(y, y_min3d, y_max3d)) {
  603.       *ex = ix;
  604.       *ey = y;
  605.       *ez = max3d_z;
  606.       return;
  607.     }
  608.       }
  609.     }
  610.       
  611.     if(iy == oy) {
  612.       /* already checked case (ix == ox && iy == oy) */
  613.       if(oz ==  iz) {
  614.       /* line parallel to x axis */
  615.  
  616.       /* assume inrange(iz, min3d_z, max3d_z) && inrange(iy, y_min3d, y_max3d) */
  617.       *ey = iy;        /* == oy */
  618.       *ez = iz;        /* == oz */
  619.       
  620.       if (inrange(x_max3d, ix, ox))
  621.         *ex = x_max3d;
  622.       else if (inrange(x_min3d, ix, ox))
  623.         *ex = x_min3d;
  624.       else {
  625.         graph_error("error in edge3d_intersect");
  626.       }
  627.  
  628.       return;
  629.       }
  630.  
  631.       /* nasty 2D slanted line in an xz plane */
  632.  
  633.       /* does it intersect x_min3d edge */
  634.       if (inrange(x_min3d, ix, ox) && x_min3d != ix && x_min3d != ox) {
  635.     z = iz + (x_min3d-ix) * ((oz-iz) / (ox-ix));
  636.     if (inrange(z, min3d_z, max3d_z)) {
  637.       *ex = x_min3d;
  638.       *ey = iy;
  639.       *ez = z;
  640.       return;
  641.     }
  642.       }
  643.  
  644.       /* does it intersect x_max3d edge */
  645.       if (inrange(x_max3d, ix, ox) && x_max3d != ix && x_max3d != ox) {
  646.     z = iz + (x_max3d-ix) * ((oz-iz) / (ox-ix));
  647.     if (inrange(z, min3d_z, max3d_z)) {
  648.       *ex = x_max3d;
  649.       *ey = iy;
  650.       *ez = z;
  651.       return;
  652.     }
  653.       }
  654.  
  655.       /* does it intersect min3d_z edge */
  656.       if (inrange(min3d_z, iz, oz) && min3d_z != iz && min3d_z != oz) {
  657.     x = ix + (min3d_z-iz) * ((ox-ix) / (oz-iz));
  658.     if (inrange(x, x_min3d, x_max3d)) {
  659.       *ex = x;
  660.       *ey = iy;
  661.       *ez = min3d_z;
  662.       return;
  663.     }
  664.       }
  665.       
  666.       /* does it intersect max3d_z edge */
  667.       if (inrange(max3d_z, iz, oz) && max3d_z != iz && max3d_z != oz) {
  668.     x = ix + (max3d_z-iz) * ((ox-ix) / (oz-iz));
  669.     if (inrange(x, x_min3d, x_max3d)) {
  670.       *ex = x;
  671.       *ey = iy;
  672.       *ez = max3d_z;
  673.       return;
  674.     }
  675.       }      
  676.     }
  677.     
  678.     if(iz == oz) {
  679.       /* already checked cases (ix == ox && iz == oz) and (iy == oy && iz == oz) */
  680.  
  681.       /* nasty 2D slanted line in an xy plane */
  682.  
  683.       /* assume inrange(oz, min3d_z, max3d_z) */
  684.  
  685.       /* does it intersect x_min3d edge */
  686.       if (inrange(x_min3d, ix, ox) && x_min3d != ix && x_min3d != ox) {
  687.     y = iy + (x_min3d-ix) * ((oy-iy) / (ox-ix));
  688.     if (inrange(y, y_min3d, y_max3d)) {
  689.       *ex = x_min3d;
  690.       *ey = y;
  691.       *ez = iz;
  692.       return;
  693.     }
  694.       }
  695.  
  696.       /* does it intersect x_max3d edge */
  697.       if (inrange(x_max3d, ix, ox) && x_max3d != ix && x_max3d != ox) {
  698.     y = iy + (x_max3d-ix) * ((oy-iy) / (ox-ix));
  699.     if (inrange(y, y_min3d, y_max3d)) {
  700.       *ex = x_max3d;
  701.       *ey = y;
  702.       *ez = iz;
  703.       return;
  704.     }
  705.       }    
  706.       
  707.       /* does it intersect y_min3d edge */
  708.       if (inrange(y_min3d, iy, oy) && y_min3d != iy && y_min3d != oy) {
  709.     x = ix + (y_min3d-iy) * ((ox-ix) / (oy-iy));
  710.     if (inrange(x, x_min3d, x_max3d)) {
  711.       *ex = x;
  712.       *ey = y_min3d;
  713.       *ez = iz;
  714.       return;
  715.     }
  716.       }
  717.       
  718.       /* does it intersect y_max3d edge */
  719.       if (inrange(y_max3d, iy, oy) && y_max3d != iy && y_max3d != oy) {
  720.     x = ix + (y_max3d-iy) * ((ox-ix) / (oy-iy));
  721.     if (inrange(x, x_min3d, x_max3d)) {
  722.       *ex = x;
  723.       *ey = y_max3d;
  724.       *ez = iz;
  725.       return;
  726.     }
  727.       }
  728.     }
  729.   
  730.     /* really nasty general slanted 3D case */
  731.  
  732.     /* does it intersect x_min3d edge */
  733.     if (inrange(x_min3d, ix, ox) && x_min3d != ix && x_min3d != ox) {
  734.       y = iy + (x_min3d-ix) * ((oy-iy) / (ox-ix));
  735.       z = iz + (x_min3d-ix) * ((oz-iz) / (ox-ix));
  736.       if (inrange(y, y_min3d, y_max3d) && inrange(z, min3d_z, max3d_z)) {
  737.     *ex = x_min3d;
  738.     *ey = y;
  739.     *ez = z;
  740.     return;
  741.       }
  742.     }
  743.  
  744.     /* does it intersect x_max3d edge */
  745.     if (inrange(x_max3d, ix, ox) && x_max3d != ix && x_max3d != ox) {
  746.       y = iy + (x_max3d-ix) * ((oy-iy) / (ox-ix));
  747.       z = iz + (x_max3d-ix) * ((oz-iz) / (ox-ix));
  748.       if (inrange(y, y_min3d, y_max3d) && inrange(z, min3d_z, max3d_z)) {
  749.     *ex = x_max3d;
  750.     *ey = y;
  751.     *ez = z;
  752.     return;
  753.       }
  754.     }
  755.  
  756.     /* does it intersect y_min3d edge */
  757.     if (inrange(y_min3d, iy, oy) && y_min3d != iy && y_min3d != oy) {
  758.       x = ix + (y_min3d-iy) * ((ox-ix) / (oy-iy));
  759.       z = iz + (y_min3d-iy) * ((oz-iz) / (oy-iy));
  760.       if (inrange(x, x_min3d, x_max3d) && inrange(z, min3d_z, max3d_z)) {
  761.     *ex = x;
  762.     *ey = y_min3d;
  763.     *ez = z;
  764.     return;
  765.       }
  766.     }
  767.  
  768.     /* does it intersect y_max3d edge */
  769.     if (inrange(y_max3d, iy, oy) && y_max3d != iy && y_max3d != oy) {
  770.       x = ix + (y_max3d-iy) * ((ox-ix) / (oy-iy));
  771.       z = iz + (y_max3d-iy) * ((oz-iz) / (oy-iy));
  772.       if (inrange(x, x_min3d, x_max3d) && inrange(z, min3d_z, max3d_z)) {
  773.     *ex = x;
  774.     *ey = y_max3d;
  775.     *ez = z;
  776.     return;
  777.       }
  778.     }
  779.  
  780.     /* does it intersect min3d_z edge */
  781.     if (inrange(min3d_z, iz, oz) && min3d_z != iz && min3d_z != oz) {
  782.       x = ix + (min3d_z-iz) * ((ox-ix) / (oz-iz));
  783.       y = iy + (min3d_z-iz) * ((oy-iy) / (oz-iz));
  784.       if (inrange(x, x_min3d, x_max3d) && inrange(y, y_min3d, y_max3d)) {
  785.     *ex = x;
  786.     *ey = y;
  787.     *ez = min3d_z;
  788.     return;
  789.       }
  790.     }
  791.  
  792.     /* does it intersect max3d_z edge */
  793.     if (inrange(max3d_z, iz, oz) && max3d_z != iz && max3d_z != oz) {
  794.       x = ix + (max3d_z-iz) * ((ox-ix) / (oz-iz));
  795.       y = iy + (max3d_z-iz) * ((oy-iy) / (oz-iz));
  796.       if (inrange(x, x_min3d, x_max3d) && inrange(y, y_min3d, y_max3d)) {
  797.     *ex = x;
  798.     *ey = y;
  799.     *ez = max3d_z;
  800.     return;
  801.       }
  802.     }
  803.  
  804.     /* If we reach here, the inrange point is on the edge, and
  805.      * the line segment from the outrange point does not cross any 
  806.      * other edges to get there. In this case, we return the inrange 
  807.      * point as the 'edge' intersection point. This will basically draw
  808.      * line.
  809.      */
  810.     *ex = ix;
  811.     *ey = iy;
  812.     *ez = iz;
  813.     return;
  814. }
  815.  
  816. /* double edge intersection algorithm */
  817. /* Given two points, both outside the plot, return
  818.  * the points where an edge of the plot intersects the line segment defined 
  819.  * by the two points. There may be zero, one, two, or an infinite number
  820.  * of intersection points. (One means an intersection at a corner, infinite
  821.  * means overlaying the edge itself). We return FALSE when there is nothing
  822.  * to draw (zero intersections), and TRUE when there is something to 
  823.  * draw (the one-point case is a degenerate of the two-point case and we do 
  824.  * not distinguish it - we draw it anyway).
  825.  */
  826. TBOOLEAN                                /* any intersection? */
  827. two_edge3d_intersect(points, i, lx, ly, lz)
  828.     struct coordinate GPHUGE *points; /* the points array */
  829.     int i;                /* line segment from point i-1 to point i */
  830.     double *lx, *ly, *lz;        /* lx[2], ly[2], lz[2]: points where it crosses edges */
  831. {
  832.     int count;
  833.     /* global x_min3d, x_max3d, y_min3d, y_max3d, min3d_z, max3d_z */
  834.     double ix = points[i-1].x;
  835.     double iy = points[i-1].y;
  836.     double iz = points[i-1].z;
  837.     double ox = points[i].x;
  838.     double oy = points[i].y;
  839.     double oz = points[i].z;
  840.     double t[6];
  841.     double swap;
  842.     double x, y, z;            /* possible intersection point */
  843.     double t_min, t_max;
  844.  
  845.     /* nasty degenerate cases, effectively drawing to an infinity point (?)
  846.        cope with them here, so don't process them as a "real" OUTRANGE point 
  847.  
  848.        If more than one coord is -VERYLARGE, then can't ratio the "infinities"
  849.        so drop out by returning FALSE */
  850.     
  851.     count = 0;
  852.     if(ix == -VERYLARGE) count++;
  853.     if(ox == -VERYLARGE) count++;
  854.     if(iy == -VERYLARGE) count++;
  855.     if(oy == -VERYLARGE) count++;
  856.     if(iz == -VERYLARGE) count++;
  857.     if(oz == -VERYLARGE) count++;
  858.  
  859.     /* either doesn't pass through 3D volume *or* 
  860.        can't ratio infinities to get a direction to draw line, so simply return(FALSE) */
  861.     if(count > 1){
  862.       return(FALSE);
  863.     }
  864.  
  865.     if(ox == -VERYLARGE || ix == -VERYLARGE)
  866.       {
  867.     if(ix == -VERYLARGE)
  868.       {
  869.         /* swap points so ix/iy/iz don't have a -VERYLARGE component */
  870.         x = ix;ix = ox;ox = x;
  871.         y = iy;iy = oy;oy = y;
  872.         z = iz;iz = oz;oz = z;
  873.       }
  874.  
  875.     /* check actually passes through the 3D graph volume */
  876.     if(ix > x_max3d && inrange(iy, y_min3d, y_max3d) && inrange(iz, min3d_z, max3d_z))
  877.       {
  878.         lx[0] = x_min3d;
  879.         ly[0] = iy;
  880.         lz[0] = iz;
  881.  
  882.         lx[1] = x_max3d;
  883.         ly[1] = iy;
  884.         lz[1] = iz;
  885.  
  886.         return(TRUE);
  887.       }
  888.     else {
  889.       return(FALSE);
  890.     }
  891.       }
  892.  
  893.     if(oy == -VERYLARGE || iy == -VERYLARGE)
  894.       {
  895.     if(iy == -VERYLARGE)
  896.       {
  897.         /* swap points so ix/iy/iz don't have a -VERYLARGE component */
  898.         x = ix; ix = ox; ox = x;
  899.         y = iy; iy = oy; oy = y;
  900.         z = iz; iz = oz; oz = z;
  901.       }
  902.  
  903.     /* check actually passes through the 3D graph volume */
  904.     if(iy > y_max3d && inrange(ix, x_min3d, x_max3d) && inrange(iz, min3d_z, max3d_z))
  905.       {
  906.         lx[0] = ix;
  907.         ly[0] = y_min3d;
  908.         lz[0] = iz;
  909.  
  910.         lx[1] = ix;
  911.         ly[1] = y_max3d;
  912.         lz[1] = iz;
  913.  
  914.         return(TRUE);
  915.       }
  916.     else {
  917.       return(FALSE);
  918.     }
  919.       }
  920.  
  921.     if(oz == -VERYLARGE || iz == -VERYLARGE)
  922.       {
  923.     if(iz == -VERYLARGE)
  924.       {
  925.         /* swap points so ix/iy/iz don't have a -VERYLARGE component */
  926.         x = ix; ix = ox; ox = x;
  927.         y = iy; iy = oy; oy = y;
  928.         z = iz; iz = oz; oz = z;
  929.       }
  930.  
  931.     /* check actually passes through the 3D graph volume */
  932.     if(iz > max3d_z && inrange(ix, x_min3d, x_max3d) && inrange(iy, y_min3d, y_max3d))
  933.       {
  934.         lx[0] = ix;
  935.         ly[0] = iy;
  936.         lz[0] = min3d_z;
  937.  
  938.         lx[1] = ix;
  939.         ly[1] = iy;
  940.         lz[1] = max3d_z;
  941.  
  942.         return(TRUE);
  943.       }
  944.     else {
  945.       return(FALSE);
  946.     }
  947.       }
  948.  
  949.     /*
  950.      * Quick outcode tests on the 3d graph volume
  951.      */
  952.  
  953.     /* 
  954.      * test z coord first --- most surface OUTRANGE points generated between
  955.      * min3d_z and z_min3d (i.e. when ticslevel is non-zero)
  956.      */
  957.     if(GPMAX(iz,oz) < min3d_z || GPMIN(iz,oz) > max3d_z)
  958.       return(FALSE);
  959.  
  960.     if(GPMAX(ix,ox) < x_min3d || GPMIN(ix,ox) > x_max3d)
  961.       return(FALSE);
  962.  
  963.     if(GPMAX(iy,oy) < y_min3d || GPMIN(iy,oy) > y_max3d)
  964.       return(FALSE);
  965.  
  966.     /*
  967.      * Special horizontal/vertical, etc. cases are checked and remaining
  968.      * slant lines are checked separately.
  969.      *
  970.      * The slant line intersections are solved using the parametric form
  971.      * of the equation for a line, since if we test x/y/z min/max planes explicitly
  972.      * then e.g. a  line passing through a corner point (x_min,y_min,z_min) 
  973.      * actually intersects all 3 planes and hence further tests would be required 
  974.      * to anticipate this and similar situations.
  975.      */
  976.  
  977.     /*
  978.      * Can have case (ix == ox && iy == oy && iz == oz) as both points OUTRANGE
  979.      */
  980.     if(ix == ox && iy == oy && iz == oz)
  981.       {
  982.     /* but as only define single outrange point, can't intersect 3D graph volume */
  983.     return(FALSE);
  984.       }
  985.  
  986.     if(ix == ox) {
  987.       if(iy == oy) {
  988.     /* line parallel to z axis */
  989.  
  990.     /* x and y coords must be in range, and line must span both min3d_z and max3d_z */
  991.     /* note that spanning min3d_z implies spanning max3d_z as both points OUTRANGE */
  992.     if(!inrange(ix, x_min3d, x_max3d) || !inrange(iy, y_min3d, y_max3d))
  993.       {
  994.         return(FALSE);
  995.       }
  996.  
  997.     if (inrange(min3d_z, iz, oz)) {
  998.       lx[0] = ix;
  999.       ly[0] = iy;
  1000.       lz[0] = min3d_z;
  1001.  
  1002.       lx[1] = ix;
  1003.       ly[1] = iy;
  1004.       lz[1] = max3d_z;
  1005.  
  1006.       return(TRUE);
  1007.     } else
  1008.       return(FALSE);
  1009.       }
  1010.       
  1011.       if(iz == oz) {
  1012.     /* line parallel to y axis */
  1013.     
  1014.     /* x and z coords must be in range, and line must span both y_min3d and y_max3d */
  1015.     /* note that spanning y_min3d implies spanning y_max3d, as both points OUTRANGE */
  1016.     if(!inrange(ix, x_min3d, x_max3d) || !inrange(iz, min3d_z, max3d_z))
  1017.       {
  1018.         return(FALSE);
  1019.       }
  1020.  
  1021.     if (inrange(y_min3d, iy, oy)) {
  1022.       lx[0] = ix;
  1023.       ly[0] = y_min3d;
  1024.       lz[0] = iz;
  1025.  
  1026.       lx[1] = ix;
  1027.       ly[1] = y_max3d;
  1028.       lz[1] = iz;
  1029.  
  1030.       return(TRUE);
  1031.     } else
  1032.       return(FALSE);
  1033.       }
  1034.  
  1035.       /* nasty 2D slanted line in a yz plane */
  1036.       
  1037.       if(!inrange(ox, x_min3d, x_max3d))
  1038.     return(FALSE);
  1039.  
  1040.       t[0] = (y_min3d - iy)/(oy - iy);
  1041.       t[1] = (y_max3d - iy)/(oy - iy);
  1042.       
  1043.       if(t[0] > t[1]) {
  1044.     swap = t[0];t[0] = t[1];t[1] = swap;
  1045.       }
  1046.  
  1047.       t[2] = (min3d_z - iz)/(oz - iz);
  1048.       t[3] = (max3d_z - iz)/(oz - iz);
  1049.       
  1050.       if(t[2] > t[3]) {
  1051.     swap = t[2];t[2] = t[3];t[3] = swap;
  1052.       }
  1053.  
  1054.       t_min = GPMAX(GPMAX(t[0],t[2]),0.0);
  1055.       t_max = GPMIN(GPMIN(t[1],t[3]),1.0);
  1056.       
  1057.       if(t_min > t_max)
  1058.     return(FALSE);
  1059.  
  1060.       lx[0] = ix;
  1061.       ly[0] = iy + t_min * (oy - iy);
  1062.       lz[0] = iz + t_min * (oz - iz);
  1063.       
  1064.       lx[1] = ix;
  1065.       ly[1] = iy + t_max * (oy - iy);
  1066.       lz[1] = iz + t_max * (oz - iz);
  1067.       
  1068.       /*
  1069.        * Can only have 0 or 2 intersection points -- only need test one coord
  1070.        */
  1071.       if(inrange(ly[0], y_min3d, y_max3d) && 
  1072.      inrange(lz[0], min3d_z, max3d_z))
  1073.     {
  1074.       return(TRUE);
  1075.     }
  1076.       
  1077.       return(FALSE);
  1078.     }
  1079.       
  1080.     if(iy == oy) {
  1081.       /* already checked case (ix == ox && iy == oy) */
  1082.       if(oz ==  iz) {
  1083.     /* line parallel to x axis */
  1084.     
  1085.     /* y and z coords must be in range, and line must span both x_min3d and x_max3d */
  1086.     /* note that spanning x_min3d implies spanning x_max3d, as both points OUTRANGE */
  1087.     if(!inrange(iy, y_min3d, y_max3d) || !inrange(iz, min3d_z, max3d_z))
  1088.       {
  1089.         return(FALSE);
  1090.       }
  1091.  
  1092.     if (inrange(x_min3d, ix, ox)) {
  1093.       lx[0] = x_min3d;
  1094.       ly[0] = iy;
  1095.       lz[0] = iz;
  1096.  
  1097.       lx[1] = x_max3d;
  1098.       ly[1] = iy;
  1099.       lz[1] = iz;
  1100.  
  1101.       return(TRUE);
  1102.     } else
  1103.       return(FALSE);
  1104.       }
  1105.  
  1106.       /* nasty 2D slanted line in an xz plane */
  1107.  
  1108.       if(!inrange(oy, y_min3d, y_max3d))
  1109.     return(FALSE);
  1110.  
  1111.       t[0] = (x_min3d - ix)/(ox - ix);
  1112.       t[1] = (x_max3d - ix)/(ox - ix);
  1113.       
  1114.       if(t[0] > t[1]) {
  1115.     swap = t[0];t[0] = t[1];t[1] = swap;
  1116.       }
  1117.  
  1118.       t[2] = (min3d_z - iz)/(oz - iz);
  1119.       t[3] = (max3d_z - iz)/(oz - iz);
  1120.       
  1121.       if(t[2] > t[3]) {
  1122.     swap = t[2];t[2] = t[3];t[3] = swap;
  1123.       }
  1124.  
  1125.       t_min = GPMAX(GPMAX(t[0],t[2]),0.0);
  1126.       t_max = GPMIN(GPMIN(t[1],t[3]),1.0);
  1127.       
  1128.       if(t_min > t_max)
  1129.     return(FALSE);
  1130.  
  1131.       lx[0] = ix + t_min * (ox - ix);
  1132.       ly[0] = iy;
  1133.       lz[0] = iz + t_min * (oz - iz);
  1134.       
  1135.       lx[1] = ix + t_max * (ox - ix);
  1136.       ly[1] = iy;
  1137.       lz[1] = iz + t_max * (oz - iz);
  1138.       
  1139.       /*
  1140.        * Can only have 0 or 2 intersection points -- only need test one coord
  1141.        */
  1142.       if(inrange(lx[0], x_min3d, x_max3d) && 
  1143.      inrange(lz[0], min3d_z, max3d_z))
  1144.     {
  1145.       return(TRUE);
  1146.     }
  1147.       
  1148.       return(FALSE);
  1149.     }
  1150.     
  1151.     if(iz == oz) {
  1152.       /* already checked cases (ix == ox && iz == oz) and (iy == oy && iz == oz) */
  1153.  
  1154.       /* nasty 2D slanted line in an xy plane */
  1155.  
  1156.       if(!inrange(oz, min3d_z, max3d_z))
  1157.     return(FALSE);
  1158.  
  1159.       t[0] = (x_min3d - ix)/(ox - ix);
  1160.       t[1] = (x_max3d - ix)/(ox - ix);
  1161.       
  1162.       if(t[0] > t[1]) {
  1163.     swap = t[0];t[0] = t[1];t[1] = swap;
  1164.       }
  1165.  
  1166.       t[2] = (y_min3d - iy)/(oy - iy);
  1167.       t[3] = (y_max3d - iy)/(oy - iy);
  1168.       
  1169.       if(t[2] > t[3]) {
  1170.     swap = t[2];t[2] = t[3];t[3] = swap;
  1171.       }
  1172.  
  1173.       t_min = GPMAX(GPMAX(t[0],t[2]),0.0);
  1174.       t_max = GPMIN(GPMIN(t[1],t[3]),1.0);
  1175.       
  1176.       if(t_min > t_max)
  1177.     return(FALSE);
  1178.  
  1179.       lx[0] = ix + t_min * (ox - ix);
  1180.       ly[0] = iy + t_min * (oy - iy);
  1181.       lz[0] = iz;
  1182.       
  1183.       lx[1] = ix + t_max * (ox - ix);
  1184.       ly[1] = iy + t_max * (oy - iy);
  1185.       lz[1] = iz;
  1186.       
  1187.       /*
  1188.        * Can only have 0 or 2 intersection points -- only need test one coord
  1189.        */
  1190.       if(inrange(lx[0], x_min3d, x_max3d) && 
  1191.      inrange(ly[0], y_min3d, y_max3d))
  1192.     {
  1193.       return(TRUE);
  1194.     }
  1195.       
  1196.       return(FALSE);
  1197.     }
  1198.   
  1199.     /* really nasty general slanted 3D case */
  1200.  
  1201.     /*
  1202.       Solve parametric equation
  1203.  
  1204.       (ix, iy, iz) + t (diff_x, diff_y, diff_z)
  1205.  
  1206.       where 0.0 <= t <= 1.0 and
  1207.  
  1208.       diff_x = (ox - ix);
  1209.       diff_y = (oy - iy);
  1210.       diff_z = (oz - iz);
  1211.      */
  1212.  
  1213.     t[0] = (x_min3d - ix)/(ox - ix);
  1214.     t[1] = (x_max3d - ix)/(ox - ix);
  1215.  
  1216.     if(t[0] > t[1]) {
  1217.       swap = t[0];t[0] = t[1];t[1] = swap;
  1218.     }
  1219.  
  1220.     t[2] = (y_min3d - iy)/(oy - iy);
  1221.     t[3] = (y_max3d - iy)/(oy - iy);
  1222.     
  1223.     if(t[2] > t[3]) {
  1224.       swap = t[2];t[2] = t[3];t[3] = swap;
  1225.     }
  1226.     
  1227.     t[4] = (iz == oz) ? 0.0 : (min3d_z - iz)/(oz - iz);
  1228.     t[5] = (iz == oz) ? 1.0 : (max3d_z - iz)/(oz - iz);
  1229.     
  1230.     if(t[4] > t[5]) {
  1231.       swap = t[4];t[4] = t[5];t[5] = swap;
  1232.     }
  1233.  
  1234.     t_min = GPMAX(GPMAX(t[0],t[2]),GPMAX(t[4],0.0));
  1235.     t_max = GPMIN(GPMIN(t[1],t[3]),GPMIN(t[5],1.0));
  1236.  
  1237.     if(t_min > t_max)
  1238.       return(FALSE);
  1239.  
  1240.     lx[0] = ix + t_min * (ox - ix);
  1241.     ly[0] = iy + t_min * (oy - iy);
  1242.     lz[0] = iz + t_min * (oz - iz);
  1243.  
  1244.     lx[1] = ix + t_max * (ox - ix);
  1245.     ly[1] = iy + t_max * (oy - iy);
  1246.     lz[1] = iz + t_max * (oz - iz);
  1247.  
  1248.     /*
  1249.      * Can only have 0 or 2 intersection points -- only need test one coord
  1250.      */
  1251.     if(inrange(lx[0], x_min3d, x_max3d) && 
  1252.        inrange(ly[0], y_min3d, y_max3d) &&
  1253.        inrange(lz[0], min3d_z, max3d_z))
  1254.       {
  1255.     return(TRUE);
  1256.       }
  1257.     
  1258.     return(FALSE);
  1259.   }
  1260.